package utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

import com.sig.MainActivity;

import android.util.Log;
import android.widget.Toast;
import myinterface.INoeud;

public class Graphe {

	private HashMap<String, Polygon> polygones = new HashMap<String, Polygon>();
	private HashMap<String, Segment> chemins = new HashMap<String, Segment>();
	
	public Graphe(){}

	/**
	 * Construction du graphe. Je suppose pour le moments que l'on reçoit des
	 * polygones qui connaissent leur chemin et ainsi de suite
	 * 
	 * @param polygones
	 * @param segments
	 */
	public Graphe(List<Polygon> polygones, List<Segment> segments) {

		this.polygones = new HashMap<String, Polygon>();
		this.chemins = new HashMap<String, Segment>();

		for (Polygon poly : polygones)
			this.polygones.put(poly.getId(), poly);

		for (Segment seg : segments)
			this.chemins.put(seg.getId(), seg);
	}

	public String toString() {
		StringBuffer st = new StringBuffer("<map>\n");

		for (Entry<String, Polygon> entry : polygones.entrySet())
			st.append(entry.getValue().toString() + "\n\n");

		for (Entry<String, Segment> entry2 : chemins.entrySet())
			st.append(entry2.getValue().toString() + "\n\n");

		st.append("</map>");
		return st.toString();
	}

	private static double poids(Point p, Point p2) {
		/*
		 * double a = Math.PI / 180; double lat1 = p.getX() * a; double lat2 =
		 * p2.getX() * a; double lon1 = p.getY() * a; double lon2 = p2.getY() *
		 * a; double t1 = Math.sin(lat1) * Math.sin(lat2); double t2 =
		 * Math.cos(lat1) * Math.cos(lat2); double t3 = Math.cos(lon1 - lon2);
		 * double t4 = t2 * t3; double t5 = t1 + t4; double rad_dist =
		 * Math.atan(-t5/Math.sqrt(-t5 * t5 +1)) + 2 * Math.atan(1); return
		 * Math.abs((rad_dist * 3437.74677 * 1.1508) * 1.6093470878864446);
		 */

		return Math.sqrt(Math.pow(p2.getX() - p.getX(), 2)
				+ Math.pow(p2.getY() - p.getY(), 2));
	}

	/**
	 * Trouve le plus court chemin entre 2 noeud du graphe, par exemple d'un
	 * batiment à un autre
	 * 
	 * @param source
	 * @param puit
	 * @return
	 */
	public List<INoeud> plusCourtChemin(INoeud source, INoeud puit) {
		Log.e("Puit", puit.toString());
		Log.e("Source", source.toString());

		if ((source.getClass() == puit.getClass())
				&& (source.getId() == puit.getId())) {
			List<INoeud> n = new ArrayList<INoeud>();
			n.add(source);
			return n;
		} else {
			if ((source instanceof Segment)
					&& (puit.getChemin().contains(source.getId()))) {
				List<INoeud> n = new ArrayList<INoeud>();
				n.add(source);
				n.add(puit);
				return n;
			} else {
				if ((source instanceof Polygon) && (puit instanceof Segment)) {
					Segment spuit = (Segment) puit;
					if (spuit.getPolygones().contains(source.getId())) {
						List<INoeud> n = new ArrayList<INoeud>();
						n.add(source);
						n.add(puit);
						return n;
					}
				}
			}
		}
		HashMap<String, Double> distance = new HashMap<String, Double>();
		HashMap<String, Boolean> parcouru = new HashMap<String, Boolean>();

		double poids, poids2;
		Segment s1, s2;

		// Initialisation
		for (Entry<String, Segment> entry : chemins.entrySet()) {
			distance.put(entry.getKey(), Double.MAX_VALUE);
			parcouru.put(entry.getKey(), false);
		}

		// Pour tous les chemin de la source je mets à 0
		for (int i = 0; i < source.getChemin().size(); i++) {
			if (distance.containsKey(source.getChemin().get(i))) {
				Log.e("Chemin initialiser", source.getChemin().get(i));
				distance.put(source.getChemin().get(i), 0.0);
				Log.e("Chemin initialiser",
						distance.get(source.getChemin().get(i)) + "");

			}
		}

		int count = 0;
		// On parcours tous les noeuds
		Log.e("Count size", this.chemins.size() + "");
		while (!allParcouru(parcouru)) {
			// Log.e("Count",count+"");

			s1 = trouve_min(parcouru, distance);
			count++;
		//	Log.e("Count", count + "");
			parcouru.put(s1.getId(), true);
			poids2 = distance.get(s1.getId());
			for (int i = 0; i < s1.getCroisements().size(); i++) {
				s2 = this.chemins.get(s1.getCroisements().get(i));
				poids = poids2 + poids(s2.getDebut(), s2.getFin()) + poids(s1.getDebut(),s2.getDebut());
				if (distance.containsKey(s2.getId())
						&& distance.get(s2.getId()) > poids) {
				//	Log.e("S2", s2.toString());
				//	Log.e("Poids s2", poids + "");
					distance.put(s2.getId(), poids);
				}
			}
		}

		// Retrouver le chemin
		ArrayList<INoeud> chemin = new ArrayList<INoeud>();
		chemin.add(puit);
		Segment tmp;
		boolean trouve = false;
		tmp = trouverMinSegment(distance, puit.getChemin());
		
		/*System.out.println("tmp " + tmp);
		for (Entry<String, Segment> ss : chemins.entrySet()) {
			Log.e("Segment", ss.getValue().toString());
			Log.e("Distance", distance.get(ss.getKey()) + "");

		}
		*/
		if(tmp != null){
			Log.e("First",tmp.toString());
		chemin.add(0, tmp);
		while (!trouve) {
			// Log.e("Je boucle ici",tmp.toString());
			if (source instanceof Polygon) {
				if(tmp != null){
				if (tmp.getPolygones() != null
						&& tmp.getPolygones().contains(source.getId())) {
					trouve = true;
				} else {
					tmp = trouverMinSegment(distance, tmp.getChemin());
					chemin.add(0, tmp);
				}
				}else{
					Toast.makeText(MainActivity.ctx, "Pas de chemin", Toast.LENGTH_SHORT).show();
					trouve = true;
				}
			} else {
				if(tmp != null){
				if (source instanceof Segment) {
					if (tmp.getChemin() != null
							&& tmp.getChemin().contains(source.getId())) {
						trouve = true;
					} else {
						tmp = trouverMinSegment(distance, tmp.getChemin());
						chemin.add(0, tmp);
					}
				}
				}else{
					Toast.makeText(MainActivity.ctx, "Pas de chemin", Toast.LENGTH_SHORT).show();
					trouve = true;	
				}
			}
		}
		}else{
			Toast.makeText(MainActivity.ctx, "Pas de chemin", Toast.LENGTH_SHORT).show();
		}
		// chemin.add(0,this.chemins.get("1107831874_0"));
		chemin.add(0, source);
		return chemin;
	}

	private Segment trouverMinSegment(HashMap<String, Double> distance,
			List<String> leschemins) {
		double min = Double.MAX_VALUE;
		Segment s = null;
		
		for (int i = 0; i < leschemins.size(); i++) {
			if (this.chemins.containsKey(leschemins.get(i))){
				if (distance.get(leschemins.get(i)) < min) {
					min = distance.get(leschemins.get(i));
					s = this.chemins.get(leschemins.get(i));
				}
			}
		}

		return s;
	}

	private Segment trouve_min(HashMap<String, Boolean> parcouru,
			HashMap<String, Double> distance) {
		double min = Double.MAX_VALUE;
		String index = "*";

		for (Entry<String, Double> entry : distance.entrySet()) {
			//System.out.println(entry.getValue());
				if (entry.getValue().doubleValue() <= min && !parcouru.get(entry.getKey())) {
					min = entry.getValue();
					index = entry.getKey();
				}
		}

		//System.out.println("min :"+index);
		
		return this.chemins.get(index);

	}

	private boolean allParcouru(HashMap<String, Boolean> parcouru) {
		for (Entry<String, Boolean> entry : parcouru.entrySet()) {
			if (!entry.getValue())
				return false;
		}
		return true;
	}

	public HashMap<String, Polygon> getPolygones() {
		return polygones;
	}

	public void setPolygones(HashMap<String, Polygon> polygones) {
		this.polygones = polygones;
	}

	public void setChemins(HashMap<String, Segment> chemins) {
		this.chemins = chemins;
	}

	public HashMap<String, Segment> getChemins() {
		return this.chemins;
	}

}
